package algorithm;

import android.Manifest;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationListener;
import com.google.gson.Gson;
import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapboxMap;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import algorithm.entity.LatLngDouglas;
import algorithm.kalman.GPSSingleData;
import algorithm.kalman.KalmanFilter;
import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;
import library.permission.PermissionsManager;
import library.permission.PermissionsResultAction;
import library.sdk.demo.R;
import mapbox.MapFragment;
import widget.utils.FileUtils;

/**
 * Created by DELL on 2017/6/5.
 */

public class TestKalmanActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener {
    @InjectView(R.id.activity_map_frame)
    FrameLayout activityMapFrame;
    @InjectView(R.id.activity_map_gps_info)
    TextView tvInfo;
    @InjectView(R.id.seekbar)
    SeekBar seekBar;
    @InjectView(R.id.seekbar_accury)
    TextView seekbarAccury;
    @InjectView(R.id.btn_start)
    Button btnStart;
    @InjectView(R.id.btn_stop)
    Button btnStop;
    @InjectView(R.id.btn_filter)
    Button btnFilter;
    @InjectView(R.id.btn_thin)
    Button btnThin;
    @InjectView(R.id.btn_save)
    Button btnSave;
    @InjectView(R.id.btn_get1)
    Button btnGet1;
    @InjectView(R.id.btn_get2)
    Button btnGet2;
    @InjectView(R.id.btn_clear)
    Button btnClear;
    @InjectView(R.id.activity_main)
    RelativeLayout activityMain;
    @InjectView(R.id.tv_min_accury)
    TextView tvMinAccury;
    @InjectView(R.id.seekbar2)
    SeekBar seekbar2;
    @InjectView(R.id.tv_thin_level)
    TextView tvThinLevel;
    @InjectView(R.id.seekbar3)
    SeekBar seekbar3;
    private MapFragment mMapFragment;
    //声明AMapLocationClient类对象
    public AMapLocationClient mLocationClient = null;
    //声明定位回调监听器


    public AMapLocationListener mLocationListener = new AMapLocationListener() {
        @Override
        public void onLocationChanged(AMapLocation aMapLocation) {


            GPSSingleData gpsSingleData = new GPSSingleData(aMapLocation.getSpeed(),
                    aMapLocation.getLatitude(), aMapLocation.getLongitude(), System.currentTimeMillis()
                    , aMapLocation.getAccuracy());
            gpsSingleDatas.add(gpsSingleData);


            String info = "经度：" + gpsSingleData.getLat() + "\n"
                    + "维度：" + gpsSingleData.getLon() + "\n" +
                    "精确度：" + gpsSingleData.getAccuracy() + "\n" +
                    "速度：" + gpsSingleData.getSpeed();
            tvInfo.setText(info);
            LatLng latlng = new LatLng(gpsSingleData.getLat(), gpsSingleData.getLon());
            addMarker(latlng, aMapLocation.getAccuracy());
        }
    };

    //声明AMapLocationClientOption对象
    public AMapLocationClientOption mLocationOption = null;


    ArrayList<Marker> markerDatas = new ArrayList<>();//过滤前
    ArrayList<Marker> newMarkerDatas = new ArrayList<>();//过滤后


    ArrayList<GPSSingleData> gpsSingleDatas = new ArrayList<>();//过滤前
    ArrayList<GPSSingleData> newGpsDatas = new ArrayList<>();//过滤后
    ArrayList<GPSSingleData> thinGpsDatas = new ArrayList<>();//抽稀后
    float rate = 0.1f;//过滤前要筛选掉10%的精确度最差的点
    float minAccuray = 3f;//优化程度
    double thinLevel = 5;//抽稀级别

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_kalman);
        ButterKnife.inject(this);
        PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(this,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        Manifest.permission.ACCESS_COARSE_LOCATION

                }, new PermissionsResultAction() {

                    @Override
                    public void onGranted() {

                    }

                    @Override
                    public void onDenied(String permission) {
                        finish();
                    }
                });

        mMapFragment = MapFragment.install(this, R.id.activity_map_frame);
        seekBar.setOnSeekBarChangeListener(this);
        seekbar2.setOnSeekBarChangeListener(this);
        seekbar3.setOnSeekBarChangeListener(this);
        initLocation();

    }

    @OnClick({R.id.btn_start, R.id.btn_stop, R.id.btn_filter, R.id.btn_save, R.id.btn_get1,
            R.id.btn_get2, R.id.btn_clear, R.id.btn_thin})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_start:
                clearData();
                start();
                break;
            case R.id.btn_stop:
                stop();
                break;
            case R.id.btn_filter:
                filter();
                break;
            case R.id.btn_thin:
                thin();
                break;
            case R.id.btn_get1:
                get(1);
                break;
            case R.id.btn_get2:
                get(2);
                break;
            case R.id.btn_clear:
                clearData();
                clearMarker();
                break;
        }
    }

    /**
     * 数据抽稀
     */
    private void thin() {
        //清空之前mark
        clear();
        //测试道格拉斯
        //得到LatLng集合
        List<LatLng> latLngs = new ArrayList<>();
        for (int i = 0; i < newGpsDatas.size(); i++) {
            GPSSingleData data = newGpsDatas.get(i);
            LatLng latLng = new LatLng(data.getLat(), data.getLon());
            latLngs.add(latLng);
        }

        //进行道格拉斯算法处理
        LatLngDouglas markerDouglas = new LatLngDouglas(latLngs);
        List<LatLng> dealAfterLatLngs = markerDouglas.compute(thinLevel);

        for (int i = 0; i < dealAfterLatLngs.size(); i++) {
            addMarker(dealAfterLatLngs.get(i), 1);
        }
        //测试道格拉斯结束
    }

    /**
     * 解决思路：
     * 1.先进行滤波 (思路一：去除一部分精确度最差的点，思路二：使用中值滤波法(过滤经度和维度两次）
     * 2.然后进行kalman平滑
     */
    public void filter() {
        KalmanFilter filter = new KalmanFilter();
        newGpsDatas.clear();
        //清空之前mark
        clear();

//        //一：滤波
        //首先去除不考虑的精确度


        //计算要过滤的点的精确度
        ArrayList<Float> ll = new ArrayList<>();
        for (GPSSingleData data :
                gpsSingleDatas) {
            ll.add(data.getAccuracy());
        }
        //将精确度按照从大到小排序
        Collections.sort(ll, Collections.<Float>reverseOrder());
        //计算20%大概是那个点
        int postion = (int) (ll.size() * rate);
        //然后得到这个点的精确度，大于这个精确度的就不要
        float accury = ll.get(postion);


        //二、kalman平滑处理
        //将定位信息进行滤波后的平滑
        for (GPSSingleData data :
                gpsSingleDatas) {
            //过滤精确度太低的点
            if (data.getAccuracy() > accury) {
                continue;
            }

            GPSSingleData gpsSingleData = filter.onLocationUpdate(data, minAccuray);


            if (gpsSingleData != null) {
                newGpsDatas.add(gpsSingleData);
            }
        }


//
//

        //中值滤波法
//        ArrayList<GPSSingleData> newGpsLats = new ArrayList<>();
////        ArrayList<GPSSingleData> newGpsLons = new ArrayList<>();
//        int m = 10;
//        for (int i = m; i < gpsSingleDatas.size() - m; i++) {
//            List<GPSSingleData> sortList = new ArrayList<GPSSingleData>(gpsSingleDatas.subList(i - m, i + m));
//            Collections.sort(sortList, new Comparator<GPSSingleData>() {
//                @Override
//                public int compare(GPSSingleData gpsSingleData, GPSSingleData t1) {
//                    if (gpsSingleData.getLat() - t1.getLat() == 0) return 0;
//                    return gpsSingleData.getLat() > t1.getLat() ? 1 : -1;//从下到大排序
//                }
//            });
//            GPSSingleData mid = sortList.get(sortList.size() / 2);
//            GPSSingleData tem = gpsSingleDatas.get(i);
//            tem.setLat(tem.getLat() - mid.getLat());
//            newGpsLats.add(tem);
//        }

//
//        for (int i = m; i < newGpsLats.size() - m; i++) {
//            List<GPSSingleData> sortList = new ArrayList<GPSSingleData>(newGpsLats.subList(i - m, i + m));
//            Collections.sort(sortList, new Comparator<GPSSingleData>() {
//                @Override
//                public int compare(GPSSingleData gpsSingleData, GPSSingleData t1) {
//                    if (gpsSingleData.getLon() - t1.getLon() == 0) return 0;
//                    return gpsSingleData.getLon() > t1.getLon() ? 1 : -1;//从下到大排序
//                }
//            });
//            GPSSingleData mid = sortList.get(sortList.size() / 2);
//            GPSSingleData tem = newGpsLats.get(i);
//            tem.setLat(tem.getLon() - mid.getLon());
//            newGpsLons.add(mid);
//        }

        //二、kalman平滑处理
        //将定位信息进行滤波后的平滑
//        for (GPSSingleData data :
//                newGpsLats) {
////            //过滤精确度太低的点
////            if (data.getAccuracy() > accury) {
////                continue;
////            }
//
//            GPSSingleData gpsSingleData = filter.onLocationUpdate(data);
//
//            if (gpsSingleData != null) {
//                newGpsDatas.add(gpsSingleData);
//            }
//        }
//        将滤波后的信息进行展示marker
        for (GPSSingleData data2 :
                newGpsDatas) {
            addMarker(new LatLng(data2.getLat(), data2.getLon()), data2.getAccuracy());
        }
        save(2);//将滤波后的轨迹保存起来
    }

    public void start() {
        //启动定位
        if (mLocationClient != null) {
            mLocationClient.startLocation();
            if (gpsSingleDatas.size() > 0) {
                gpsSingleDatas.clear();
            }
        }
    }

    public void clearMarker() {
        MapboxMap mapboxMap = mMapFragment.getMapboxMap();
        mapboxMap.clear();
        gpsSingleDatas.clear();
    }

    /**
     * @param type 1. yuantu 2. 过滤后的图
     */
    public void save(int type) {

        try {
            File file = null;
            if (type == 1) {
                file = new File(getObbDir() + "/" + getClass().getSimpleName() + "/" + "before.json");
                JSONArray array = new JSONArray();
                for (int i = 0; i < gpsSingleDatas.size(); i++) {
                    GPSSingleData data = gpsSingleDatas.get(i);
                    JSONObject item = new JSONObject();
                    item.put("speed", data.getSpeed());
                    item.put("lon", data.getLon());
                    item.put("lat", data.getLat());
                    item.put("timestamp", data.getTimestamp());
                    item.put("accuracy", data.getAccuracy());
                    array.put(item);
                }
                if (FileUtils.saveText(file, array.toString()))
                    Toast.makeText(this, "以保存" + file, Toast.LENGTH_SHORT).show();
                else
                    Toast.makeText(this, "失败", Toast.LENGTH_SHORT).show();
            } else {
                file = new File(getObbDir() + "/" + getClass().getSimpleName() + "/" + "filter.json");
                JSONArray array = new JSONArray();
                for (int i = 0; i < newGpsDatas.size(); i++) {
                    GPSSingleData data = newGpsDatas.get(i);
                    JSONObject item = new JSONObject();
                    item.put("speed", data.getSpeed());
                    item.put("lon", data.getLon());
                    item.put("lat", data.getLat());
                    item.put("timestamp", data.getTimestamp());
                    item.put("accuracy", data.getAccuracy());
                    array.put(item);
                }
                if (FileUtils.saveText(file, array.toString()))
                    Toast.makeText(this, "以保存" + file, Toast.LENGTH_SHORT).show();
                else
                    Toast.makeText(this, "失败", Toast.LENGTH_SHORT).show();
            }


        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public void get(int type) {
        clear();
        ArrayList<GPSSingleData> lists = new ArrayList<>();
        File file;
        if (type == 1) {
            file = new File(getObbDir() + "/" + getClass().getSimpleName() + "/" + "before.json");
            gpsSingleDatas.clear();
        } else {
            file = new File(getObbDir() + "/" + getClass().getSimpleName() + "/" + "filter.json");
        }
        String json = getFileFromSD(file.getAbsolutePath());
        try {
            JSONArray jsonArray = new JSONArray(json);
            Toast.makeText(this, "显示的点数：" + jsonArray.length() + "", Toast.LENGTH_SHORT).show();
            for (int i = 0; i < jsonArray.length(); i++) {
                JSONObject jsonObject = new JSONObject(jsonArray.get(i).toString());
                GPSSingleData data = new Gson().fromJson(jsonObject.toString(), GPSSingleData.class);
                lists.add(data);
                if (type == 1) {
                    gpsSingleDatas.add(data);
                }
            }
            MapboxMap mapboxMap = mMapFragment.getMapboxMap();
            mapboxMap.clear();
            //将滤波后的信息进行展示marker
            for (GPSSingleData data2 :
                    lists) {
                addMarker(new LatLng(data2.getLat(), data2.getLon()), data2.getAccuracy());
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }
    }


    private String getFileFromSD(String path) {
        String result = "";

        try {
            FileInputStream f = new FileInputStream(path);
            BufferedReader bis = new BufferedReader(new InputStreamReader(f));
            String line = "";
            while ((line = bis.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;

    }

    private void addMarker(LatLng latLng, float accuracy) {
        MapboxMap mapboxMap = mMapFragment.getMapboxMap();
//        for (Marker marker : mapboxMap.getMarkers()) {
//            LatLng position = marker.getPosition();
//            if (position.getLongitude() == latLng.getLongitude()
//                    && position.getLatitude() == latLng.getLatitude()) {
//                return;
//            }
//        }
        mapboxMap.addMarker(new MarkerViewOptions()
                .position(latLng)
                .snippet(
                        latLng.getLatitude() + "\n"
                                + latLng.getLongitude() + "\n"
                                + accuracy + "\n"
                ));
        if (mapboxMap.getMarkers().size() == 1)
            mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 20));
        else
            mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(latLng));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mLocationClient.onDestroy();//销毁定位客户端，同时销毁本地定位服务。
    }

    public void stop() {
        mLocationClient.stopLocation();//停止定位后，本地定位服务并不会被销毁
        save(1);

    }

    private void initLocation() {
        //初始化定位
        mLocationClient = new AMapLocationClient(getApplicationContext());
        //设置定位回调监听
        mLocationClient.setLocationListener(mLocationListener);
        //初始化AMapLocationClientOption对象
        mLocationOption = new AMapLocationClientOption();
        //设置定位模式为AMapLocationMode.Hight_Accuracy，高精度模式。
        mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
        //设置定位间隔,单位毫秒,默认为2000ms，最低1000ms。
        mLocationOption.setInterval(1000);

        //设置是否强制刷新WIFI，默认为true，强制刷新。
//        mLocationOption.setWifiActiveScan(false);
        //设置是否允许模拟位置,默认为false，不允许模拟位置
//        mLocationOption.setMockEnable(false);
        //单位是毫秒，默认30000毫秒，建议超时时间不要低于8000毫秒。
//        mLocationOption.setHttpTimeOut(20000);

        //关闭缓存机制
        mLocationOption.setLocationCacheEnable(false);

        //给定位客户端对象设置定位参数
        mLocationClient.setLocationOption(mLocationOption);
    }

//    float accury = 100;

    @Override
    public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
        if (seekBar == this.seekBar) {
            rate = i * 1f / 100f;
            seekbarAccury.setText("过滤点比例：" + i + "");
        } else if (seekBar == this.seekbar2) {
            minAccuray = i;
            tvMinAccury.setText("优化程度：" + i);
        } else if (seekBar == this.seekbar3) {
            thinLevel = i;
            tvThinLevel.setText("抽稀程度： " + i);
        }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {

    }


    private void clearData() {
        gpsSingleDatas.clear();
        newGpsDatas.clear();
    }

    private void clear() {
        //清空之前mark
        MapboxMap mapboxMap = mMapFragment.getMapboxMap();
        mapboxMap.clear();
    }
}
